Newer
Older
BlackoutClient / Assets / Best HTTP / Source / Connections / HTTPConnection.cs
#if !UNITY_WEBGL || UNITY_EDITOR

using System;

using BestHTTP.Core;

namespace BestHTTP.Connections
{
    /// <summary>
    /// Represents and manages a connection to a server.
    /// </summary>
    public sealed class HTTPConnection : ConnectionBase
    {
        public TCPConnector connector;
        public IHTTPRequestHandler requestHandler;

        public override TimeSpan KeepAliveTime {
            get {
                if (this.requestHandler != null && this.requestHandler.KeepAlive != null)
                    return this.requestHandler.KeepAlive.TimeOut;
        
                return base.KeepAliveTime;
            }
        
            protected set
            {
                base.KeepAliveTime = value;
            }
        }

        public override bool CanProcessMultiple
        {
            get
            {
                if (this.requestHandler != null)
                    return this.requestHandler.CanProcessMultiple;
                return base.CanProcessMultiple;
            }
        }

        internal HTTPConnection(string serverAddress)
            :base(serverAddress)
        {}

        internal override void Process(HTTPRequest request)
        {
            this.LastProcessedUri = request.CurrentUri;

            if (this.requestHandler == null || !this.requestHandler.HasCustomRequestProcessor)
                base.Process(request);
            else
            {
                this.requestHandler.Process(request);
                LastProcessTime = DateTime.Now;
            }
        }

        protected override void ThreadFunc()
        {
            if (this.connector != null && !this.connector.IsConnected)
            {
                // this will semd the request back to the queue
                RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(CurrentRequest, RequestEvents.Resend));
                ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed));
                return;
            }

            if (this.connector == null)
            {
                this.connector = new Connections.TCPConnector();

                try
                {
                    this.connector.Connect(this.CurrentRequest);
                }
                catch(Exception ex)
                {
                    HTTPManager.Logger.Exception("HTTPConnection", "Connector.Connect", ex);

                    this.CurrentRequest.State = HTTPRequestStates.ConnectionTimedOut;
                    ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed));

                    return;
                }

#if !NETFX_CORE
                // data sending is buffered for all protocols, so when we put data into the socket we want to send them asap
                this.connector.Client.NoDelay = true;
#endif
                StartTime = DateTime.UtcNow;

                HTTPManager.Logger.Information("HTTPConnection", "Negotiated protocol through ALPN: '" + this.connector.NegotiatedProtocol + "'");

                switch (this.connector.NegotiatedProtocol)
                {
                    case HTTPProtocolFactory.W3C_HTTP1:
                        this.requestHandler = new Connections.HTTP1Handler(this);
                        ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HostProtocolSupport.HTTP1));
                        break;

#if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_ALTERNATE_SSL && !BESTHTTP_DISABLE_HTTP2
                    case HTTPProtocolFactory.W3C_HTTP2:
                        this.requestHandler = new Connections.HTTP2.HTTP2Handler(this);
                        this.CurrentRequest = null;
                        ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HostProtocolSupport.HTTP2));
                        break;
#endif

                    default:
                        HTTPManager.Logger.Error("HTTPConnection", "Unknown negotiated protocol: " + this.connector.NegotiatedProtocol);
                        RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(CurrentRequest, RequestEvents.Resend));
                        ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed));
                        return;
                }
            }

            this.requestHandler.RunHandler();
            LastProcessTime = DateTime.Now;
        }

        public override void Shutdown(ShutdownTypes type)
        {
            base.Shutdown(type);

            if (this.requestHandler != null)
                this.requestHandler.Shutdown(type);

            switch(this.ShutdownType)
            {
                case ShutdownTypes.Immediate:
                    this.connector.Dispose();
                    break;
            }
        }

        protected override void Dispose(bool disposing)
        {
            LastProcessedUri = null;
            if (this.State != HTTPConnectionStates.WaitForProtocolShutdown)
            {
                if (this.connector != null)
                {
                    try
                    {
                        this.connector.Close();
                    }
                    catch
                    { }
                    this.connector = null;
                }

                if (this.requestHandler != null)
                {
                    try
                    {
                        this.requestHandler.Dispose();
                    }
                    catch
                    { }
                    this.requestHandler = null;
                }
            }
            else
            {
                // We have to connector to do not close its stream at any cost while disposing. 
                // All references to this connection will be removed, so this and the connector may be be finalized after some time.
                // But, finalizing (and disposing) the connector while the protocol is still active would be fatal, 
                //  so we have to make sure that it will not happen. This also means that the protocol has the resposibility (as always had)
                //  to close the stream and TCP connection properly.
                if (this.connector != null)
                    this.connector.LeaveOpen = true;
            }

            base.Dispose(disposing);
        }
    }
}

#endif